Workforce Planning (Expression Form)

$$ \begin{array}{rll} \displaystyle \min_{x\in \mathbb{R}^7} & x_1 + x_2 + x_3 + x_4 + x_5 + x_6 + x_7 \\ \,{\rm s.t.} & \begin{array}[t]{rcl} x_1 + x_4 + x_5 + x_6 + x_7 & \geq & 14 \\ x_1 + x_2 + x_5 + x_6 + x_7 & \geq & 13 \\ x_1 + x_2 + x_3 + x_6 + x_7 & \geq & 15 \\ x_1 + x_2 + x_3 + x_4 + x_7 & \geq & 16 \\ x_1 + x_2 + x_3 + x_4 + x_5 & \geq & 19 \\ x_2 + x_3 + x_4 + x_5 + x_6 & \geq & 18 \\ x_3 + x_4 + x_5 + x_6 + x_7 & \geq & 11 \\ x_i & \geq & 0 ~~~~ \forall ~i \end{array} \end{array} $$

In [1]:
# Import PuLP modeler functions
try:
    from pulp import *
except:
    !pip install pulp

In [2]:
# Create the 'prob' variable to contain the problem data
prob = LpProblem("Workforce Planning Problem",LpMinimize)

In [3]:
# All variables are created with a lower limit of zero
x1=LpVariable("x1",0)
x2=LpVariable("x2",0)
x3=LpVariable("x3",0)
x4=LpVariable("x4",0)
x5=LpVariable("x5",0)
x6=LpVariable("x6",0)
x7=LpVariable("x7",0)

# The objective function is added to 'prob' first
prob += x1 + x2 + x3 + x4 + x5 + x6 + x7, "Total number of workers"

# The five constraints are entered
prob += x1 + x4 + x5 + x6 + x7 >= 14.0, "People that work on Monday"
prob += x1 + x2 + x5 + x6 + x7 >= 13.0, "People that work on Tuesday"
prob += x1 + x2 + x3 + x6 + x7 >= 15.0, "People that work on Wednesday"
prob += x1 + x2 + x3 + x4 + x7 >= 16.0, "People that work on Thursday"
prob += x1 + x2 + x3 + x4 + x5 >= 19.0, "People that work on Friday"
prob += x2 + x3 + x4 + x5 + x6 >= 18.0, "People that work on Saturday"
prob += x3 + x4 + x5 + x6 + x7 >= 11.0, "People that work on Sunday"

# The problem data is written to an .lp file
#prob.writeLP("WorkforceModel.lp")

# The problem is solved using PuLP's choice of Solver
prob.solve()


Out[3]:
1

In [4]:
# The status of the solution is printed to the screen
print("Status: %s" % LpStatus[prob.status])


Status: Optimal

In [5]:
prob


Out[5]:
Workforce Planning Problem:
MINIMIZE
1*x1 + 1*x2 + 1*x3 + 1*x4 + 1*x5 + 1*x6 + 1*x7 + 0
SUBJECT TO
People_that_work_on_Monday: x1 + x4 + x5 + x6 + x7 >= 14

People_that_work_on_Tuesday: x1 + x2 + x5 + x6 + x7 >= 13

People_that_work_on_Wednesday: x1 + x2 + x3 + x6 + x7 >= 15

People_that_work_on_Thursday: x1 + x2 + x3 + x4 + x7 >= 16

People_that_work_on_Friday: x1 + x2 + x3 + x4 + x5 >= 19

People_that_work_on_Saturday: x2 + x3 + x4 + x5 + x6 >= 18

People_that_work_on_Sunday: x3 + x4 + x5 + x6 + x7 >= 11

VARIABLES
x1 Continuous
x2 Continuous
x3 Continuous
x4 Continuous
x5 Continuous
x6 Continuous
x7 Continuous

In [6]:
# Each of the variables is printed with it's resolved optimum value
for v in prob.variables():
    print("%s = %.2f" % (v.name, v.varValue))


x1 = 4.00
x2 = 7.00
x3 = 1.00
x4 = 4.00
x5 = 3.00
x6 = 3.00
x7 = 0.00

In [7]:
# The optimised objective function value is printed to the screen
print("Total number of workers = %f" % value(prob.objective))


Total number of workers = 22.000000

In [8]:
for x in prob.variables():
    print ("Name:        %s" % x.name)
    print ("Value:       %f" % x.varValue)
    print ("Category:    %s" % x.cat)
    print ("Lower Bound: %f" % x.lowBound)
    print ("Upper Bound: %s\n" % x.upBound)


Name:        x1
Value:       4.000000
Category:    Continuous
Lower Bound: 0.000000
Upper Bound: None

Name:        x2
Value:       7.000000
Category:    Continuous
Lower Bound: 0.000000
Upper Bound: None

Name:        x3
Value:       1.000000
Category:    Continuous
Lower Bound: 0.000000
Upper Bound: None

Name:        x4
Value:       4.000000
Category:    Continuous
Lower Bound: 0.000000
Upper Bound: None

Name:        x5
Value:       3.000000
Category:    Continuous
Lower Bound: 0.000000
Upper Bound: None

Name:        x6
Value:       3.000000
Category:    Continuous
Lower Bound: 0.000000
Upper Bound: None

Name:        x7
Value:       0.000000
Category:    Continuous
Lower Bound: 0.000000
Upper Bound: None


In [9]:
for (name,constraint) in prob.constraints.items():
    print ("Name:        ", name)
    print ("Constraint:  ", constraint)
    print ("Lower Bound: ", constraint.getLb())
    print ("Upper Bound: ", constraint.getUb())
    print ("Value:       ", pulp.value(constraint))
    print ("Sense:       ", pulp.LpConstraintSenses[constraint.sense])
    print ("Constant:    ", constraint.constant)
    print ("Slack:       ", constraint.slack)
    print ("Slack (Feas):", constraint.slack if constraint.sense < 0 else -constraint.slack)
    print (" ")


('Name:        ', 'People_that_work_on_Monday')
('Constraint:  ', 1*x1 + 1*x4 + 1*x5 + 1*x6 + 1*x7 + -14.0 >= 0)
('Lower Bound: ', 14.0)
('Upper Bound: ', None)
('Value:       ', 0.0)
('Sense:       ', '>=')
('Constant:    ', -14.0)
('Slack:       ', -0.0)
('Slack (Feas):', 0.0)
 
('Name:        ', 'People_that_work_on_Tuesday')
('Constraint:  ', 1*x1 + 1*x2 + 1*x5 + 1*x6 + 1*x7 + -13.0 >= 0)
('Lower Bound: ', 13.0)
('Upper Bound: ', None)
('Value:       ', 4.0)
('Sense:       ', '>=')
('Constant:    ', -13.0)
('Slack:       ', -4.0)
('Slack (Feas):', 4.0)
 
('Name:        ', 'People_that_work_on_Wednesday')
('Constraint:  ', 1*x1 + 1*x2 + 1*x3 + 1*x6 + 1*x7 + -15.0 >= 0)
('Lower Bound: ', 15.0)
('Upper Bound: ', None)
('Value:       ', 0.0)
('Sense:       ', '>=')
('Constant:    ', -15.0)
('Slack:       ', -0.0)
('Slack (Feas):', 0.0)
 
('Name:        ', 'People_that_work_on_Thursday')
('Constraint:  ', 1*x1 + 1*x2 + 1*x3 + 1*x4 + 1*x7 + -16.0 >= 0)
('Lower Bound: ', 16.0)
('Upper Bound: ', None)
('Value:       ', 0.0)
('Sense:       ', '>=')
('Constant:    ', -16.0)
('Slack:       ', -0.0)
('Slack (Feas):', 0.0)
 
('Name:        ', 'People_that_work_on_Friday')
('Constraint:  ', 1*x1 + 1*x2 + 1*x3 + 1*x4 + 1*x5 + -19.0 >= 0)
('Lower Bound: ', 19.0)
('Upper Bound: ', None)
('Value:       ', 0.0)
('Sense:       ', '>=')
('Constant:    ', -19.0)
('Slack:       ', -0.0)
('Slack (Feas):', 0.0)
 
('Name:        ', 'People_that_work_on_Saturday')
('Constraint:  ', 1*x2 + 1*x3 + 1*x4 + 1*x5 + 1*x6 + -18.0 >= 0)
('Lower Bound: ', 18.0)
('Upper Bound: ', None)
('Value:       ', 0.0)
('Sense:       ', '>=')
('Constant:    ', -18.0)
('Slack:       ', -0.0)
('Slack (Feas):', 0.0)
 
('Name:        ', 'People_that_work_on_Sunday')
('Constraint:  ', 1*x3 + 1*x4 + 1*x5 + 1*x6 + 1*x7 + -11.0 >= 0)
('Lower Bound: ', 11.0)
('Upper Bound: ', None)
('Value:       ', 0.0)
('Sense:       ', '>=')
('Constant:    ', -11.0)
('Slack:       ', -0.0)
('Slack (Feas):', 0.0)
 

In [10]:
x1.varValue + x2.varValue + x3.varValue + x4.varValue + x5.varValue + x6.varValue + x7.varValue


Out[10]:
22.0